package dpe;

import static org.lwjgl.opengl.GL11.*;

import java.util.ArrayList;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.util.glu.GLU;

public class Game {
	private boolean run = true;
	private ArrayList<World> worlds;
	private World currentWorld;
	private String title;
	private int width = 1, height = 1;
	private int syncFPS = 60;
	private String startWorld;
	private boolean fpsControl = false;
	private boolean sync = false;
	private long lastFrame, lastFPS;
	private int frames, currentFPS;
	private boolean displayFPS = true;
	private float fov = 40;
	private float zNear = 0.1f;
	private float zFar = 1000.0f;
	private String verision;
	private boolean fullscreen = false;

	public Game() {
		worlds = new ArrayList<World>();
	}

	public void init() {
	}

	public void start() {
		Engine.log(0, "Starting game");
		try {
			Display.setDisplayMode(new DisplayMode(width, height));
			Display.setTitle(title);
			Display.setFullscreen(fullscreen);
			Display.create();
		} catch (LWJGLException e) {
			e.printStackTrace();
		}

		if (startWorld != null) {
			setWorld(startWorld);
		}

		lastFPS = Engine.getTime();

		new Thread(new Runnable() {
			public void run() {
				while (run) {
					int delta = getDelta();
					update(delta);

					render();
					Display.update();
					if (displayFPS) {
						Display.setTitle(title + " | FPS: " + currentFPS);
					}
					if (sync)
						Display.sync(syncFPS);

					if (Display.isCloseRequested()) {
						stop();
					}
				}
				Engine.log(0, "Game closed");
				Engine.exit(0);
			}
		}).run();
	}

	public void stop() {
		run = false;
	}

	public void addWorld(World world) {
		for (int i = 0; i < worlds.size(); i++) {
			if (worlds.get(i).getName().toLowerCase().equals(world.getName().toLowerCase())) {
				Engine.log(2, "Could not add world '" + world.getName() + "': Two worlds with the same name");
				Engine.exit(1);
			}
		}
		worlds.add(world);
		Engine.log(0, "Added world '" + world.getName() + "'");
	}

	public void setWorld(String name) {
		for (int i = 0; i < worlds.size(); i++) {
			if (worlds.get(i).getName().toLowerCase().equals(name.toLowerCase())) {
				Engine.log(0, "World set to '" + name + "'");
				return;
			}
		}
		Engine.log(1, "Could not find world '" + name + "'");
	}

	public void update(int delta) {
		if (currentWorld != null)
			currentWorld.update();

		if (!Mouse.isGrabbed() && fpsControl) {
			Mouse.setGrabbed(true);
		}

		if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
			setFPSControl(false);
		}

		if (Mouse.isButtonDown(0)) {
			setFPSControl(true);
		}

		if (Engine.getTime() - lastFPS > 1000) {
			currentFPS = frames;
			frames = 0;
			lastFPS += 1000;
		}
		frames++;
	}

	private void initGL() {
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		GLU.gluPerspective(fov, width / height, zNear, zFar);
		glMatrixMode(GL_MODELVIEW);

		glEnable(GL_DEPTH_TEST);
	}

	public void render() {
		initGL();
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		if (currentWorld != null)
			currentWorld.render();
	}

	public int getDelta() {
		return (int) (Engine.getTime() - lastFrame);
	}

	public void setDisplayWidth(int w) {
		width = w;
		Engine.log(0, "Display width set to " + w);
	}

	public void setDisplayHeight(int h) {
		height = h;
		Engine.log(0, "Display height set to " + h);
	}

	public void setDisplayTitle(String t) {
		this.title = t;
		Engine.log(0, "Display title set to " + t);
	}

	public void setStartWorld(String t) {
		startWorld = t;
	}

	public void setFPSControl(boolean b) {
		fpsControl = b;
		Mouse.setGrabbed(b);
	}

	public boolean hasFPSControl() {
		return fpsControl;
	}

	public void setFov(float fov) {
		this.fov = fov;
	}

	public void setZNear(float zNear) {
		this.zNear = zNear;
	}

	public void setZFar(float zFar) {
		this.zFar = zFar;
	}

	public void setVerision(String verision) {
		this.verision = verision;
	}

	public void setDisplayFullscreen(boolean b) {
		fullscreen = b;
	}
}
